/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

#include "canvas_action.h"

#include "acsprite.h"
#include "actools.h"
#include "aircraft_agent.h"
#include "canvas_tools.h"
#include "canvasview.h"
#include "engine_impl.h"
#include "score.h"
#include "waypointsprite.h"

#include <qapplication.h>
#include <qdialog.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qpushbutton.h>
//Added by qt3to4:
#include <Q3HBoxLayout>
#include <QMouseEvent>
#include <QEvent>
#include <Q3VBoxLayout>
#include <Q3BoxLayout>

#include <algorithm>

using namespace atc;
using std::make_pair;
using std::string;


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CanvasEventHandler
//------------------------------------------------------------------------------

/*!
 *
 */
CanvasEventHandler::CanvasEventHandler(CanvasView*	a_view, 
									   Q3CanvasItem* a_item) 
	:	QObject(a_view),
		m_view(a_view), 
		m_item(a_item)
{
}

void 
CanvasEventHandler::unload() 
{ 
	m_view->unloadEventHandler(this); 
}

void 
CanvasEventHandler::clear_tool() 
{ 
	m_view->set_tool_key(0); 
}

void 
CanvasEventHandler::update() 
{ 
	m_view->canvas()->update(); 
}

void 
CanvasEventHandler::activate_pointer(bool a_activate) 
{ 
	m_view->_pointer = a_activate;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ScaleHandler : CanvasEventHandler
//------------------------------------------------------------------------------

/*!
 *
 */
ScaleHandler::ScaleHandler( CanvasView *v )
		: CanvasEventHandler(v)
		, _scale(0)
		, _cursor()
{}

/*!
 *
 */
void ScaleHandler::setItem( Q3CanvasItem *i ) {
	_scale = dynamic_cast<ScaleLine *>(i)->getXHair();
}

/*!
 * Process events pertaining to the Scale sprite.
 *
 * QEvent::Type { MouseButtonPress, MouseButtonRelease, MouseMove }
 * Qt::ButtonState { LeftButton }
 */
bool ScaleHandler::action( QEvent *e ) {
	QMouseEvent *me = dynamic_cast<QMouseEvent*>(e);

	if ( !me )
			return false;

	QPoint p = m_view->inverseWorldMatrix().map( me->pos() );

	switch ( me->type() ) {

	case QEvent::MouseButtonPress:
		if ( Qt::LeftButton != me->button() )
				return false;
		activate_pointer( true );
		_cursor = p;
		break;

	case QEvent::MouseButtonRelease:
		if ( Qt::LeftButton != me->button() )
				return false;
		activate_pointer( false );
		unload();
		break;

	case QEvent::MouseMove:
		_scale->move_by( p - _cursor );
		update();
		_cursor = p;
		break;

	default:
		return false;
	}

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RotateCalloutHandler : CanvasEventHandler
//------------------------------------------------------------------------------

/*!
 *
 */
bool RotateCalloutHandler::action( QEvent *e ) {
	ACLocator *ac = dynamic_cast<ACLocator*>( m_item );
	if( ac ) ac->sprite()->rotate_callout();
	unload();
	return ac;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// AltitudeHandler : CanvasEventHandler
//
//--------------------------------------------------------------------------------------------------
// class
//

const int AltitudeHandler::RESOLUTION_ALTITUDE_DELTA = 1000;

//--------------------------------------------------------------------------------------------------
// construction
//
AltitudeHandler::AltitudeHandler(CanvasView* a_view, 
									AircraftDeclaration a_ad,
									int a_x, 
									int a_y) 
		: CanvasEventHandler(a_view),
			m_isDeclaring(false),
			m_agent(0),
			m_dialog(0),
			m_callsign1(NULL),
			m_callsign2(NULL),
			m_conflict(NULL),
			m_nonConflict(NULL),
			m_x(a_x),
			m_y(a_y),
			m_actionStates(),
			m_aircraftDeclaration(a_ad)
{
	buildDialog();

	m_actionStates.push_back(CS_ACCEPTED);
	m_actionStates.push_back(CS_OVEROUT);
	m_actionStates.push_back(CS_HANDOFF);
}

//--------------------------------------------------------------------------------------------------
// [public]
//
void 
AltitudeHandler::setItem(Q3CanvasItem* a_item)
{
	m_item = a_item;
	m_agent = dynamic_cast<ACAltBlk*>(a_item)->sprite()->agent();
	m_callsign1->setText(m_agent->callsign());
}

bool 
AltitudeHandler::action(QEvent* a_event) 
{
	QMouseEvent* me = dynamic_cast<QMouseEvent*>(a_event);

	if (!me) 
	{
		return false;
	}

	if (QEvent::MouseButtonDblClick == me->type())
	{
		// accept but do nothing
		return true;
	}

	if (QEvent::MouseButtonPress != me->type() || Qt::LeftButton != me->button())
	{
		return false;
	}

	if (std::find(m_actionStates.begin(), m_actionStates.end(), 
		m_agent->control()) == m_actionStates.end())
	{
		return false;
	}

	if (!m_isDeclaring) 
	{
		conflictDeclaration();

		KeyValues kvs;
		kvs.push_back(make_pair(string("action"),			string("altitude")));
		kvs.push_back(make_pair(string("declare_conflict"), string(m_callsign1->text())));
		m_view->record_event(kvs);
	}
	else 
	{
		// The conflict declaration box is already showing
		// The user has selected the second aircraft to declare as a conflict/non-conflict

		Q3CanvasItem* item = m_view->find_item(me->pos());
		ACSprite* sprite = dynamic_cast<ACSprite*>(item);

		if (!sprite || m_agent == sprite->sprite()->agent())
		{
			return false;
		}

		m_callsign2->setText(sprite->sprite()->agent()->callsign());

		if (m_conflict)
		{
			m_conflict->setEnabled(true);
		}

		if (m_nonConflict)
		{
			m_nonConflict->setEnabled(true);
		}

		KeyValues kvs;
		kvs.push_back(make_pair(string("action"),					string("altitude")));
		kvs.push_back(make_pair(string("declare_conflict_type"),	string(m_callsign2->text())));
		m_view->record_event(kvs);
	}

	return true;
}

//--------------------------------------------------------------------------------------------------
// [public slots]
//
void 
AltitudeHandler::conflictDeclared() 
{
	KeyValues kvs;
	kvs.push_back(make_pair(string("action"),				string("altitude")));
	kvs.push_back(make_pair(string("result"),				string("conflict_declared")));
	kvs.push_back(make_pair(string("primary_aircraft"),		string(m_callsign1->text())));
	kvs.push_back(make_pair(string("secondary_aircraft"),	string(m_callsign2->text())));
	m_view->record_event(kvs);

	emit sig_conflict_identified(	string(m_callsign1->text()), 
									string(m_callsign2->text()));

	cleanUp();
}

void 
AltitudeHandler::nonConflictDeclared() 
{
	KeyValues kvs;
	kvs.push_back(make_pair(string("action"),				string("altitude")));
	kvs.push_back(make_pair(string("result"),				string("non_conflict_declared")));
	kvs.push_back(make_pair(string("primary_aircraft"),		string(m_callsign1->text())));
	kvs.push_back(make_pair(string("secondary_aircraft"),	string(m_callsign2->text())));
	m_view->record_event(kvs);

	emit sig_non_conflict_identified(	string(m_callsign1->text()), 
										string(m_callsign2->text()));

	cleanUp();
}


void
AltitudeHandler::reject()
{ 
	KeyValues kvs;
	kvs.push_back(make_pair(string("action"),				string("altitude")));
	kvs.push_back(make_pair(string("result"),				string("reject")));
	kvs.push_back(make_pair(string("primary_aircraft"),		string(m_callsign1->text())));
	kvs.push_back(make_pair(string("secondary_aircraft"),	string(m_callsign2->text())));
	m_view->record_event(kvs);

	cleanUp(); 
}

void 
AltitudeHandler::resolve() 
{ 
	KeyValues kvs;
	kvs.push_back(make_pair(string("action"),	string("altitude")));
	kvs.push_back(make_pair(string("resolve"),	to_string("RESOLUTION_ALTITUDE_DELTA")));
	kvs.push_back(make_pair(string("aircraft"),	m_agent->callsign()));
	kvs.push_back(make_pair(string("control"),	to_string(m_agent->control())));
	m_view->record_event(kvs);

	m_agent->climb(RESOLUTION_ALTITUDE_DELTA);
}


//--------------------------------------------------------------------------------------------------
// [private]
//
void 
AltitudeHandler::conflictDeclaration() 
{
	m_isDeclaring = true;
	m_dialog->show();
}

void 
AltitudeHandler::cleanUp() 
{
	m_dialog->hide();
	m_isDeclaring = false;
	m_callsign1->setText("");
	m_callsign2->setText("");

	if (m_conflict)
	{
		m_conflict->setEnabled(false);
	}

	if (m_nonConflict)
	{
		m_nonConflict->setEnabled(false);
	}

	unload();
}

void
AltitudeHandler::GetDisplayText(const std::string& a_text,
								std::vector<std::string>& a_tokens,
								const string& delimiters)
{
	// Skip delimiters at the beginning
	std::string::size_type last_pos = a_text.find_first_not_of(delimiters, 0);

	// Find first non-delimiter
	std::string::size_type pos = a_text.find_first_of(delimiters, last_pos);

	while (std::string::npos != pos || std::string::npos != last_pos)
	{
		// Found a token, add it to the vector
		a_tokens.push_back(a_text.substr(last_pos, pos - last_pos));
		
		if (pos != std::string::npos)
		{
			a_tokens.push_back(delimiters);
		}

		// Skip delimiters
		last_pos = a_text.find_first_not_of(delimiters, pos);

		// Find next non-delimiter
		pos = a_text.find_first_of(delimiters, last_pos);
	}
}

void 
AltitudeHandler::buildDialog() 
{
	m_dialog = new QDialog(m_view, 
							"Conflict Declaration", 
							0, 
							Qt::WStyle_Customize | Qt::WStyle_NoBorder);

	if ((m_x > 0) && (m_y > 0))
	{
		m_dialog->move(m_x, m_y);
	}
	else 					
	{
		m_dialog->move(20, qApp->mainWidget()->height() / 2);
	}

	m_callsign1 = new QLabel(m_dialog);
	m_callsign2 = new QLabel(m_dialog);
	QFont font = m_dialog->font();
	font.setFamily(LISTBOX_FONT_FAMILY);
	font.setPointSize(LISTBOX_FONT_POINT);
	m_dialog->setFont(font);

	font.setBold(true);

	m_dialog->resize(0, 0);
	Q3BoxLayout *l = new Q3VBoxLayout(m_dialog, 5);
	QPushButton* cancel = NULL;

	Q3BoxLayout *row0 = new Q3HBoxLayout(l);
	Q3BoxLayout *row1 = new Q3HBoxLayout(l);
	unsigned short acCount = 0;

	for (std::vector<AircraftDeclarationText>::iterator textIt = m_aircraftDeclaration.m_texts.begin();
			textIt != m_aircraftDeclaration.m_texts.end(); textIt++)
	{
		unsigned short rowNumber = textIt->m_row;
		std::vector<std::string> displayText;
		GetDisplayText(textIt->m_text, displayText, AC_SUBSTITUTE_STRING);

		for (std::vector<std::string>::iterator it = displayText.begin();
				it != displayText.end(); it++)
		{
			Q3BoxLayout* row;

			if (rowNumber == 0)
			{
				row = row0;
			}
			else if (rowNumber == 1)
			{
				row = row1;
			}

			if (*it != AC_SUBSTITUTE_STRING)
			{
				row->addWidget(new QLabel((*it).c_str(), m_dialog));
			}
			else
			{
				acCount++;

				if (acCount == 1)
				{
					row->addWidget(m_callsign1);
				}
				else if (acCount == 2)
				{
					row->addWidget(m_callsign2);
				}
			}
		}
	}

	Q3BoxLayout* bmgr = new Q3HBoxLayout(l);

	for (std::vector<AircraftDeclarationButton>::iterator buttonIt = m_aircraftDeclaration.m_buttons.begin();
			buttonIt != m_aircraftDeclaration.m_buttons.end(); buttonIt++)
	{
		ADB_TYPE type = buttonIt->m_type;

		switch (type)
		{
		case ADB_TYPE_CONFLICT:
			m_conflict = new QPushButton(buttonIt->m_label.c_str(), m_dialog);
			m_conflict->setEnabled(false);
			bmgr->addWidget(m_conflict);
			QObject::connect(m_conflict,	SIGNAL(clicked()), this, SLOT(conflictDeclared()));
			break;

		case ADB_TYPE_NON_CONFLICT:
			m_nonConflict = new QPushButton(buttonIt->m_label.c_str(), m_dialog);
			m_nonConflict->setEnabled(false);
			bmgr->addWidget(m_nonConflict);
			QObject::connect(m_nonConflict, SIGNAL(clicked()), this, SLOT(nonConflictDeclared()));
			break;

		case ADB_TYPE_CANCEL:
			cancel = new QPushButton(buttonIt->m_label.c_str(), m_dialog);
			bmgr->addWidget(cancel);
			QObject::connect(cancel,		SIGNAL(clicked()), this, SLOT(reject()));
			break;
		default:
			break;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// AltitudeHandlerBuilder : CanvasEventHandlerBuilder
//
//--------------------------------------------------------------------------------------------------
// [public] api
//
CanvasEventHandler* 
AltitudeHandlerBuilder::operator()(CanvasView* a_canvasView, 
								   EngineImpl* a_engine) 
{ 
	AltitudeHandler* hdlr = new AltitudeHandler(a_canvasView, m_aircraftDeclaration, _x, _y);

	if (a_engine->Type() != pact::TRIAL_TASK_PRACTICE)
	{
		QObject::connect(	hdlr, 
							SIGNAL(sig_conflict_identified(const std::string&, const std::string&)),
							a_engine->get_score(), 
							SLOT(DeclareConflict(const std::string&, const std::string&)));
		QObject::connect(	hdlr, 
							SIGNAL(sig_non_conflict_identified(const std::string&, const std::string&)),
							a_engine->get_score(), 
							SLOT(DeclareNonConflict(const std::string&, const std::string&)));

		//QObject::connect(	a_engine->get_score(), SIGNAL(sig_hit()), hdlr, SLOT(resolve()));
	}

	return hdlr; 
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// VelocityHandler : CanvasEventHandler
//
//--------------------------------------------------------------------------------------------------

/*!
 *
 */
VelocityHandler::VelocityHandler( CanvasView *v ) 
		: CanvasEventHandler(v)
		, _agent(0)
		, _dialog(0)
{
	build_dialog();
}

/*!
 *
 */
void VelocityHandler::setItem( Q3CanvasItem *i ) {
	m_item = i;
	_agent = dynamic_cast<ACVelBlk*>(i)->sprite()->agent();
}

/*!
 *
 */
bool VelocityHandler::action( QEvent *e ) {
/*
	case CanvasItem::RTTI_VELBLK:
		select_velocity( dynamic_cast< ACVelBlk* >( item ) );
		break;

	show_selector( _vel_select, _cursor, ac->sprite()->agent() );
*/

	return true;
}

void 
VelocityHandler::resolve() 
{ 
}

/*!
 *
 */
void VelocityHandler::build_dialog() {
	_dialog = 0;
}

/*!
 *
 */
void VelocityHandler::accept() {
	unload();
}

/*!
 *
 */
void VelocityHandler::reject() {
	unload();
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ACToolsHandler : CanvasEventHandler
//------------------------------------------------------------------------------

/*!
 *
 */
bool ACToolsHandler::action( QEvent *e ) {
	QMouseEvent *me = dynamic_cast<QMouseEvent*>(e);
	if ( !me ) return false;
	if ( QEvent::MouseButtonPress != me->type() || Qt::LeftButton != me->button() )
			return true;

	unload();

	AircraftSprite* sprite = dynamic_cast<ACLocator*>(m_item)->sprite();

	switch ( m_view->tool() ) {
	case CanvasItem::ACCEPT_TOOL:	sprite->accept();			clear_tool();		break;
	case CanvasItem::HANDOFF_TOOL:	sprite->handoff();			clear_tool();		break;
	case CanvasItem::HISTORY_TOOL:	sprite->toggle_history();	break;
	case CanvasItem::SRPROBE_TOOL:	sprite->toggle_probe();		break;
	case CanvasItem::ROUTE_TOOL:	sprite->toggle_route();		break;
	}

	return true;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PauseToolHandler : CanvasEventHandler
//--------------------------------------------------------------------------------------------------

/*!
 *
 */
void PauseToolHandler::onLoad() { m_view->pause(); }


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ResumeToolHandler : CanvasEventHandler
//--------------------------------------------------------------------------------------------------

/*!
 *
 */
void ResumeToolHandler::onLoad() { m_view->resume(); }


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// VectorToolHandler : CanvasEventHandler
//------------------------------------------------------------------------------

/*!
 *
 */
VectorToolHandler::VectorToolHandler( CanvasView *v )
		: CanvasEventHandler(v)
		, _vector(0)
{}

/*!
 *
 */
bool VectorToolHandler::action( QEvent *e ) {
	QMouseEvent *me = dynamic_cast<QMouseEvent*>(e);

	if ( !me )
			return false;

	QPoint pt = m_view->inverseWorldMatrix().map( me->pos() );

	switch ( me->type() ) {

	case QEvent::MouseButtonPress: {
			Q3CanvasItem *item = m_view->find_item( me->pos() );
			ACSprite *ac = dynamic_cast<ACSprite*>(item);
			if ( !ac ) {
				unload();
				return false;
			}

			if ( Qt::LeftButton != me->button() ) return false;
			activate_pointer( true );
			_vector = m_view->atc_canvas()->add_vector( ac->sprite() );
			break;
		}
	case QEvent::MouseButtonRelease: {
			if ( Qt::LeftButton != me->button() )
					return false;
			activate_pointer( false );
			unload();

			Q3CanvasItem *item = m_view->find_item( me->pos() );
			ACTRouteLine *line = dynamic_cast<ACTRouteLine*>(item);

			if ( line && line->aircraft() == _vector->aircraft() ) 
					line->reroute( pt );
			else if ( _vector->is_valid() ) 
					_vector->action();

			m_view->atc_canvas()->cancel_vector();
			_vector = 0;

			break;
		}
	case QEvent::MouseMove:
		_vector->set_end( pt.x(), pt.y() );
		update();
		break;

	default:
		return false;
	}

	return true;
}

/*!
 *
 */
void VectorToolHandler::unload() {
	clear_tool();
	CanvasEventHandler::unload();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// BRLToolHandler : CanvasEventHandler
//------------------------------------------------------------------------------

/*!
 *
 */
BRLToolHandler::BRLToolHandler( CanvasView *v ) 
		: CanvasEventHandler(v), _brline(0) 
{}

/*!
 *
 */
void BRLToolHandler::setItem( Q3CanvasItem *i ){
}

/*!
 *
 */
bool BRLToolHandler::action( QEvent *e ) {
	QMouseEvent *me = dynamic_cast<QMouseEvent*>(e);
	if ( !me ) return false;

	QPoint pt = m_view->inverseWorldMatrix().map( me->pos() );

	switch ( me->type() ) {

	case QEvent::MouseButtonPress: {
			if ( Qt::LeftButton != me->button() ) return false;
			Q3CanvasItem *item = m_view->find_item( me->pos() );
			CanvasItem::TYPE type = item 
					? static_cast<CanvasItem::TYPE>(item->rtti()) 
					: CanvasItem::RTTI_NONE;
			switch (type) {

			case CanvasItem::RTTI_BRLDATA:
				_brline = dynamic_cast<BRLDataSprite*>(item)->select();
				break;

			case CanvasItem::RTTI_WPLOCATOR: 
				_brline = m_view->atc_canvas()->add_brl( dynamic_cast<WPLocator*>(item)->waypoint() );
				break;

			case CanvasItem::RTTI_LOCATOR:
				_brline = m_view->atc_canvas()->add_brl( dynamic_cast<ACSprite*>(item)->sprite() );
				break;

			case CanvasItem::RTTI_NONE:
				_brline = m_view->atc_canvas()->add_brl( pt.x(), pt.y() );
				break;

			default:
				return true;
			}

			activate_pointer( true );

			break;
		}
	case QEvent::MouseButtonRelease: {
			if ( Qt::LeftButton != me->button() ) return false;
			activate_pointer( false );
			//unload();

			Q3CanvasItem *item = m_view->find_item( me->pos() );
			CanvasItem::TYPE type = item 
					? static_cast<CanvasItem::TYPE>(item->rtti()) 
					: CanvasItem::RTTI_NONE;
			switch (type) {

			case CanvasItem::RTTI_WPLOCATOR: 
				_brline->hook( dynamic_cast<WPLocator*>(item)->waypoint() );
				break;

			case CanvasItem::RTTI_LOCATOR:
				_brline->hook( dynamic_cast<ACSprite*>(item)->sprite() );
				break;

			case CanvasItem::RTTI_NONE:
			default:
				break;
			}

			_brline = 0;
			break;
		}
	case QEvent::MouseMove:
		_brline->hook( pt );
		update();
		break;

	case QEvent::MouseButtonDblClick: {
			Q3CanvasItem *item = m_view->find_item( me->pos() );
			if ( CanvasItem::RTTI_BRLDATA == item->rtti() )
					dynamic_cast<BRLDataSprite*>(item)->kill();
		}

	default:
		return false;
	}

	return true;
}


//--------------------------------------------------------------------------------------------------
